/*
 * Copyright [2009] [Marcin Rzeźnicki]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
 */
package mr.go.yaxc.actions;

import mr.go.yaxc.Action;
import mr.go.yaxc.ConfigurationError;
import mr.go.yaxc.ScopeHandler;

import org.apache.commons.beanutils.BeanUtils;
import org.xml.sax.Attributes;

/**
 * This action sets property on <i>object</i> in local scope. Property value is
 * created from <i>class</i> attribute, or, if it is missing, referenced from
 * the global context by <i>ref</i>
 * 
 * <h6>on xml element start:</h6>If <i>class</i> attribute is set: Creates new
 * instance of <i>class</i> using default constructor and sets it as
 * <i>object</i> in current scope<br/>
 * If <i>class</i> attribute is missing, then this action obtains reference to
 * an object from the global context, using <i>ref</i>, and sets it as
 * <i>object</i> in current scope <h6>on xml element end:</h6> Sets property on
 * <i>parentObject</i> in scope. Property name is taken from xml tag. Property
 * value is <i>object</i> <h6>
 * prerequisites:</h6> <i>parentObject</i> set <h6>attributes:</h6>
 * <table>
 * <tr>
 * <th>Attribute name</th>
 * <th>What is it for?</th>
 * <th>Required?</th>
 * </tr>
 * <tr>
 * <td><i>class</i></td>
 * <td>qualified classname of instance to be created</td>
 * <td>no</td>
 * </tr>
 * <tr>
 * <td><i>ref</i></td>
 * <td>if set, does not create an instance but obtains one from the global
 * context</td>
 * <td>no</td>
 * </tr>
 * </table>
 *<p>
 * Example:<br>
 * (from logback manual)
 * 
 * <pre>
 * &lt;appender name=&quot;STDOUT&quot; class=&quot;mr.go.yaxc.tests.test3.Appender&quot;&gt;
 * 		&lt;layout class=&quot;mr.go.yaxc.tests.test3.Layout&quot;&gt;
 * 			&lt;Pattern&gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&lt;/Pattern&gt;
 *     &lt;/layout&gt;
 *   &lt;/appender&gt;
 * </pre>
 * 
 * (assume that this instance is matched to node 'layout')
 * </p>
 * 
 * 
 * @author Marcin Rzeźnicki
 * 
 */
public class SetObjectProperty extends Action {

    private String propertyName;

    @Override
    public void carryOut(String name, Attributes attributes,
                         ScopeHandler handler)
            throws Exception {
        String classname = attributes.getValue(getAttribute("class"));
        String refid = attributes.getValue(getAttribute("ref"));
        if (classname == null && refid == null) {
            throw new ConfigurationError("Missing attributes: " + getAttribute(
                    "class") + ", " + getAttribute("ref"));
        }
        if (refid != null) {
            object = handler.getReference(refid);
        } else {
            object = createInstance(classname);
            if (handler.getLogger().isDebugEnabled()) {
                handler.getLogger().debug("New: {}",
                                          object.toString());
            }
        }
        propertyName = name;
        // common mistake
        if (Character.isUpperCase(propertyName.charAt(0))) {
            propertyName = Character.toLowerCase(propertyName.charAt(0)) +
                    propertyName.substring(1);
        }
    }

    @Override
    public void processed(String body, ScopeHandler handler)
            throws Exception {
        final Object o = handler.parentObject();
        if (o == null) {
            throw new ConfigurationError("No object to set property of");
        }
        if (handler.getLogger().isDebugEnabled()) {
            handler.getLogger().debug("Setting property {} of object ({})",
                                      propertyName,
                                      o);
        }
        BeanUtils.setProperty(o,
                              propertyName,
                              object);
    }
}
